The aim is to explore the clustering and label transfers for the sample SCPCS000168.
In order to explore the clustering results, we look into some marker genes, pathways enrichment and label transfer.
This approach would provide us a rapid idea of the quality of the clustering:
# The base path for the OpenScPCA repository, found by its (hidden) .git directory
repository_base <- rprojroot::find_root(rprojroot::is_git_root)
# The current data directory, found within the repository base directory
data_dir <- file.path(repository_base, "data", "current", params$scpca_project_id)
# The path to this module
module_base <- file.path(repository_base, "analyses", "cell-type-wilms-tumor-06")In this notebook, we are working with the Wilms tumor sample defined
in params$sample_id from the Wilms tumor dataset
param$scpca_project_id. We work with the pre-processed and
labeled Seurat object that is the output of
02b_label-transfer_fetal_kidney_reference_Stewart.Rmd saved
in the results directory.
To explore the clustering results, we look into some marker genes reported here:
Reports will be saved in the notebook directory. The
pre-processed and annotated Seurat object per samples are
saved in the result folder.
Here we defined function that will be used multiple time all along the notebook.
For a Seurat object objectand a metadata
metadata, the function visualize_metadata will
plot FeaturePlot and BarPlot
object is the Seurat object
metadata the gene or quantitative value to be
plotted
group.by is the metadata used for grouping the
violin plots
visualize_metadata <- function(object, meta, group.by){
if(is.numeric(object@meta.data[,meta])){
d <- SCpubr::do_FeaturePlot(object,
features = meta,
pt.size = 0.2,
legend.width = 0.5,
legend.length = 5,
legend.position = "right") + ggtitle(meta)
b <- SCpubr::do_ViolinPlot(srat,
features = meta,
ncol = 1,
group.by = group.by,
legend.position = "none")
return(d + b + plot_layout(ncol = 2, widths = c(2,4)))
}
else{
d <- SCpubr::do_DimPlot(object, reduction="umap", group.by = group.by, label = TRUE, repel = TRUE) + ggtitle(paste0(meta," - umap")) + theme(text=element_text(size=18))
b <- SCpubr::do_BarPlot(sample = object,
group.by = meta,
split.by = group.by,
position = "fill",
font.size = 10,
legend.ncol = 3) +
ggtitle("% cells")+
xlab(print(group.by)) +
theme(text=element_text(size=18))
return(d + b + plot_layout(ncol = 2, widths = c(2,4)))
}
}For a Seurat object objectand a features
features, the function visualize_feature will
plot FeaturePlot and ViolinPlot
object is the Seurat object
features the gene or quantitative value to be
plotted
group.by is the metadata used for grouping the
violin plots
visualize_feature <- function(object, features, group.by = "seurat_clusters"){
feature_symbol <- AnnotationDbi::select(org.Hs.eg.db,
keys=features,
columns="SYMBOL",
keytype="ENSEMBL")
d <- SCpubr::do_FeaturePlot(object,
features = feature_symbol$ENSEMBL,
pt.size = 0.2,
legend.width = 0.5,
legend.length = 5,
legend.position = "right") + ggtitle(feature_symbol$SYMBOL)
b <- SCpubr::do_ViolinPlot(srat,
features = feature_symbol$ENSEMBL,
ncol = 1,
group.by = group.by,
legend.position = "none",
assay = "SCT") + ylab(feature_symbol$SYMBOL)
return(d + b + plot_layout(ncol = 2, widths = c(2,4)))
}ModuleScore from a MSigDB
datasetFor a Seurat object object, the function
MSigDB_score will calculate a score using
AddModuleScore() function for a MSigDB gene set
gs_name in the category category
object is the Seurat object to calculate the score.
Score will be added in the metadata of this object.
category is a MSigDB collection (https://www.gsea-msigdb.org/gsea/msigdb/collections.jsp).
values in
c("H", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8")
gs_name is the name of a MSigDB gene set,
e.g. "HALLMARK_P53_PATHWAY"
name is the name of the module
MSigDB_score <- function(object, category, gs_name, name){
set <- msigdbr(species = "human", category = category)
set_list <- set %>%
dplyr::filter(gs_name == gs_name) %>%
dplyr::distinct(gs_name, gene_symbol, human_ensembl_gene) %>% as.data.frame()
set_list <- list(set_list$human_ensembl_gene)
object <- AddModuleScore(object, features = set_list, name = name)
return(object)
}Enrichment_plotaim to perform enrichment of the marker
genes for each of the Seurat clusters and summarize the results in a
dotplot.
category is the MSigDB category or collection to be
used values in
c("H", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8")
signatures is a list of marker genes per
cluster
backgroundis the universe used for
enrichment
Enrichment_plot <- function(category, signatures, background){
## define genesets
tryCatch( expr = {
gene_set <- msigdbr(species = "human", category = category)
msigdbr_set <- gene_set %>% dplyr::distinct(gs_name, ensembl_gene) %>% as.data.frame()
cclust<-compareCluster(geneCluster = signatures,
fun = enricher,
TERM2GENE = msigdbr_set,
universe=background)
d <- dotplot(cclust,showCategory=15) + scale_y_discrete(labels=function(x) str_wrap(x, width=40))
return(d)
},
error = function(e) { print("No enrichment") } )
}do_SankeyPlot is largely inspired by the source code
from SCpubr as the function has been removed from the
package. https://github.com/enblacar/SCpubr/blob/v1.1.2-dev-stable/R/do_SankeyPlot.R
first_group is a metadata variable. First group of
nodes of the sankey plot.
last_group is a metadata variable. Last group of
nodes of the sankey plot.
middle_groups is a metadata variable. Vector of
groups of nodes of the sankey plot.
type is a type of plot to make. One of:
sankey: Generates a sankey plot.alluvial: Generated an Alluvial plot, a kind of sankey
plot where all groups have the same height.width Width of the nodes.
space Vertical space between the nodes. It appears
to be equal to a single cell. Use big numbers to see a difference (like,
1000 or 10000).
position GGplot2 position.
node.fill Color to fill the nodes.
node.color Color for the contour of the
nodes.
flow.alpha Alpha of the connections.
flow.color Color for the contour of the
connections.
text_size Size of the labels.
text_color Color of the labels.
smooth How smooth the connections are.
use_labels Whether to use labels or text for the
node names.
hjust General hjust for the labels.
do_SankeyPlot <- function(sample,
first_group,
last_group,
type = "sankey",
middle_groups = NULL,
width = 0.1,
space = ifelse(type == "sankey", 0.05 * ncol(sample), 0),
position = "identity",
node.fill = "white",
node.color = "white",
flow.alpha = 0.75,
flow.color = "black",
text_size = 3,
text_color = "black",
font.size = 18,
font.type = "sans",
smooth = 8,
use_labels = FALSE,
hjust = NULL,
colors.first = NULL,
colors.middle = NULL,
colors.last = NULL,
plot.title = NULL,
plot.subtitle = NULL,
plot.caption = NULL){
# Check logical parameters.
logical_list <- list("use_labels" = use_labels)
# Check numeric parameters.
numeric_list <- list("width" = width,
"space" = space,
"flow.alpha" = flow.alpha,
"text_size" = text_size,
"font.size" = font.size,
"smooth" = smooth,
"hjust" = hjust)
# Check character parameters.
character_list <- list("first_group" = first_group,
"last_group" = last_group,
"middle_groups" = middle_groups,
"type" = type,
"position" = position,
"node.color" = node.color,
"flow.color" = flow.color,
"text_color" = text_color,
"font.type" = font.type,
"colors.first" = colors.first,
"colors.middle" = colors.middle,
"colors.last" = colors.last,
"node.fill" = node.fill,
"plot.title" = plot.title,
"plot.subtitle" = plot.subtitle,
"plot.caption" = plot.caption)
`%>%` <- magrittr::`%>%`
data <- suppressWarnings({sample@meta.data %>%
dplyr::select(dplyr::all_of(c(first_group, middle_groups, last_group))) %>%
tibble::rownames_to_column(var = "cell") %>%
dplyr::select(-.data$cell) %>%
ggsankey::make_long(dplyr::all_of(c(first_group, middle_groups, last_group))) %>%
dplyr::rowwise() %>%
dplyr::mutate(hjust = if(.data$x %in% middle_groups){0.5}
else if (.data$x == last_group){0}
else if (.data$x == first_group){1})})
if (!is.null(hjust)){data$hjust <- hjust}
colors.first <- SCpubr::do_ColorPalette(colors.use = "steelblue",
n = length(unique(sample@meta.data[, first_group])))
if (is.factor(sample@meta.data[, first_group])){
names(colors.first) <- levels(sample@meta.data[, first_group])
} else {
names(colors.first) <- sort(unique(sample@meta.data[, first_group]))
}
colors.last <- SCpubr::do_ColorPalette(colors.use = "steelblue",
n = length(unique(sample@meta.data[, last_group])))
if (is.factor(sample@meta.data[, last_group])){
names(colors.last) <- levels(sample@meta.data[, last_group])
} else {
names(colors.last) <- sort(unique(sample@meta.data[, last_group]))
}
colors.use <- c(colors.first, colors.last)
func_use <- ifelse(isTRUE(use_labels), ggsankey::geom_sankey_label, ggsankey::geom_sankey_text)
p <- data %>%
ggplot2::ggplot(mapping = ggplot2::aes(x = .data$x,
next_x = .data$next_x,
node = .data$node,
next_node = .data$next_node,
fill = factor(.data$node),
label = .data$node,
hjust = .data$hjust)) +
ggsankey::geom_sankey(flow.alpha = flow.alpha,
node.color = node.color,
node.fill = node.fill,
color = flow.color,
width = width,
position = position,
type = type,
space = space) +
func_use(size = text_size,
color = text_color,
fontface = "bold",
position = position,
type = type,
space = space) +
ggplot2::scale_fill_manual(values = colors.use) +
ggplot2::xlab("") +
ggplot2::ylab("") +
ggplot2::labs(title = plot.title,
subtitle = plot.subtitle,
caption = plot.caption) +
ggplot2::theme_minimal(base_size = font.size) +
ggplot2::theme(axis.title = ggplot2::element_text(color = "black",
face = "bold"),
axis.line.x = ggplot2::element_blank(),
axis.text.x = ggplot2::element_text(color = "black",
face = "bold",
angle = 0,
hjust = 0.5,
vjust = 1),
axis.text.x.top = ggplot2::element_text(color = "black",
face = "bold",
angle = 0,
hjust = 0.5,
vjust = 1),
axis.text.y = ggplot2::element_blank(),
axis.ticks = ggplot2::element_blank(),
panel.grid.major = ggplot2::element_blank(),
plot.title.position = "plot",
plot.title = ggplot2::element_text(face = "bold", hjust = 0),
plot.subtitle = ggplot2::element_text(hjust = 0),
plot.caption = ggplot2::element_text(hjust = 1),
panel.grid = ggplot2::element_blank(),
text = ggplot2::element_text(family = font.type),
plot.caption.position = "plot",
legend.text = ggplot2::element_text(face = "bold"),
legend.position = "none",
legend.title = ggplot2::element_text(face = "bold"),
legend.justification = "center",
plot.margin = ggplot2::margin(t = 10, r = 10, b = 10, l = 10),
plot.background = ggplot2::element_rect(fill = "white", color = "white"),
panel.background = ggplot2::element_rect(fill = "white", color = "white"),
legend.background = ggplot2::element_rect(fill = "white", color = "white"),
strip.text =ggplot2::element_text(color = "black", face = "bold"))
return(p)
}do_Table_Heatmap shows heatmap of counts of cells in
groups of metadata
data seurat objectfirst_group is the name of the first metadata to group
the cellslast_group is the name of the second metadata to group
the cellsdo_Table_Heatmap <- function(data, first_group, last_group ){
df <- data@meta.data %>%
mutate_if(sapply(data@meta.data, is.character), as.factor) %>%
group_by( !!sym(first_group), !!sym(last_group))%>%
summarise(Nb = n())
p <- ggplot(df, aes(x= !!sym(first_group), y = !!sym(last_group), fill = Nb)) +
geom_tile() +
scale_fill_viridis(discrete=FALSE) +
theme_bw() +
theme(text = element_text(size = 20))
return(p)
}Seurat objectWe expect up to 5 set of clusters:
d2 <- SCpubr::do_DimPlot(srat, reduction="umap", group.by = "seurat_clusters", label = TRUE) + ggtitle("Seurat Cluster - umap")
d1 <- SCpubr::do_DimPlot(srat, reduction="pca", group.by = "seurat_clusters", label = TRUE) + ggtitle("Seurat Cluster - pca")
v1 <- SCpubr::do_ViolinPlot(srat, features = c( "subsets_mito_percent"), ncol = 1, group.by = "seurat_clusters", legend.position = "none")
v2 <- SCpubr::do_ViolinPlot(srat, features = c( "detected"), ncol = 1, group.by = "seurat_clusters", legend.position = "none")
v3 <- SCpubr::do_ViolinPlot(srat, features = c( "sum"), ncol = 1, group.by = "seurat_clusters", legend.position = "none")
d1 + d2 + (v1 + v2 +v3 + plot_layout(ncol=1) ) + plot_layout(ncol = 3, widths = c(2,2,4))s.genes <- srat@assays$RNA@meta.data$gene_ids[srat@assays$RNA@meta.data$gene_symbol %in% cc.genes$s.genes]
g2m.genes <- srat@assays$RNA@meta.data$gene_ids[srat@assays$RNA@meta.data$gene_symbol %in% cc.genes$g2m.genes]
srat <- CellCycleScoring(srat, s.features = s.genes, g2m.features = g2m.genes, set.ident = FALSE)## [1] "seurat_clusters"
Here, we open the table of marker genes marker-sets/CellType_metadata.csv. Note: we do not expect to have a clear and nice pattern of expression for all of the following markers in every tumor. This is just ti get a few idea.
for(feature in CellType_metadata$ENSEMBL_ID[CellType_metadata$ENSEMBL_ID %in% rownames(srat@assays$SCT)]){
print(visualize_feature(srat, features = feature, group.by = "seurat_clusters"))
}here we will calculate a TP53 score using AddMduleScore and the genes of the HALLMARK_P53_PATHWAY gene set.
srat <- MSigDB_score(object = srat , category = "H", gs_name = "HALLMARK_P53_PATHWAY", name = "TP53_score")
visualize_metadata(srat, meta = "TP53_score1", group.by = "seurat_clusters" )here we will calculate a DNA_repair score using AddMduleScore and the genes of the HALLMARK_DNA_REPAIR gene set.
srat <- MSigDB_score(object = srat , category = "H", gs_name = "HALLMARK_DNA_REPAIR", name = "DNA_repair_score")
visualize_metadata(srat, meta = "DNA_repair_score1", group.by = "seurat_clusters" )Note: Chemo-treated samples should have higher DNA-damage scores.
In addition to the list of known marker genes, we used an unbiased approach to find transcripts that characterized the different clusters.
We run DElegate::FindAllMarkers2 to find markers of the different clusters and manually check if they do make sense. DElegate::FindAllMarkers2 is an improved version of Seurat::FindAllMarkers based on pseudobulk differential expression method. Please check the preprint from Chistoph Hafemeister: https://www.biorxiv.org/content/10.1101/2023.03.28.534443v1 and tool described here: https://github.com/cancerbits/DElegate
Of note, we won’t use it for annotation, this is just here to get an idea!
feature_conversion <- srat@assays$RNA@meta.data
de_results <- DElegate::FindAllMarkers2(srat, group_column = "seurat_clusters")
#filter the most relevant markers
s.markers <- de_results[de_results$padj < params$padj_thershold & de_results$log_fc > params$lfc_threshold & de_results$rate1 > params$rate1_threshold,]
# add gene symbol for easiest interpretation of the result
s.markers$gene_ids <- s.markers$feature
s.markers <- left_join(s.markers,feature_conversion, by = c( "gene_ids") )
stopifnot(
"Error joining gene ids and feature names" = identical(s.markers$feature, s.markers$gene_ids)
)DT::datatable(s.markers, caption = ("marker genes"),
extensions = 'Buttons',
options = list( dom = 'Bfrtip',
buttons = c( 'csv', 'excel')))# Select top 5 genes for heatmap plotting
s.markers <- na.omit(s.markers)
s.markers %>%
group_by(group1) %>%
top_n(n = 5, wt = log_fc) -> top5
# subset for plotting
cells <- WhichCells(srat, downsample = 100)
ss <- subset(srat, cells = cells)
ss <- ScaleData(ss, features = top5$feature)
p1 <- SCpubr::do_DimPlot(srat, reduction="umap", group.by = "seurat_clusters", label = TRUE, repel = TRUE) + ggtitle("Seurat Cluster - umap")
p2 <- DoHeatmap(ss, features = top5$feature, cells = cells, group.by = "seurat_clusters") + NoLegend() +
scale_fill_gradientn(colors = c("#01665e","#35978f",'darkslategray3', "#f7f7f7", "#fee391","#fec44f","#F9AD03"))
p3 <- ggplot(srat@meta.data, aes(seurat_clusters, fill = seurat_clusters)) + geom_bar() + NoLegend()
common_title <- sprintf("Unsupervised clustering %s, %d cells", srat@meta.data$orig.ident[1], ncol(srat))
show((((p1 / p3) + plot_layout(heights = c(3,2)) | p2) ) + plot_layout(widths = c(1, 2)) + plot_layout(heights = c(3,1)) + plot_annotation(title = common_title))Here we perform enrichment analysis of the marker genes found in the previous section for each Seurat cluster.
We defined as universe/background all the genes expressed in the dataset, meaning the rownames of the Seurat object.
We used three gene sets from MSiGDB:
We used enricher function from clusterProfiler to perform enrichment analysis.
# define background genes = universe for enrichment
background <- rownames(srat)
# Define gene signature per cluster
signatures <- list()
for( i in unique(s.markers$group1)){
signatures[[paste0("cluster ",i)]] <- s.markers$feature[s.markers$group1 == i]
}EMT signature should be enriched in stroma cluster. E2F/proliferation should be enriched in blastema cluster. MYC(N), TP53 must be enriched in blastema cluster.
The MSigDB C8 gene set is quite relevant for kidney and nephroblastoma annotations. Epithelial (cancer and normal) cells should be enriched in mature/adulte kidney pathways while blastema cancer cells will show enrichment of fetal kidney development pathway / cap mesenchyme.
Here, we quickly checked annotations that are present in the _processed rds object. However, the automated annotation have not been performed using a cancer specific reference or a kidney reference. We do not expect a nice labelling of the cells as the overlap of cell types between the reference and the query dataset is poor. This support the need to do a proper label transfer from the fetal kidney atlas, which is imho the best reference that can be applied to a Wilms tumor query.
## [1] "seurat_clusters"
## [1] "seurat_clusters"
## [1] "seurat_clusters"
## [1] "seurat_clusters"
## [1] "seurat_clusters"
## [1] "seurat_clusters"
## [1] "seurat_clusters"
markers = list("malignant" = CellType_metadata$ENSEMBL_ID[CellType_metadata$cell_class == "malignant"],
"immune"= CellType_metadata$ENSEMBL_ID[CellType_metadata$cell_class == "immune"],
"endothelium"= CellType_metadata$ENSEMBL_ID[CellType_metadata$cell_class == "endothelium"],
"non-malignant"= CellType_metadata$ENSEMBL_ID[CellType_metadata$cell_class == "non-malignant"])
p1 <- SCpubr::do_DotPlot(sample = srat,
group.by = "fetal_kidney_predicted.compartment",
features = markers,
axis.text.x.angle = 90,
flip = TRUE,
plot.title = "fetal_kidney_predicted.compartment"
) ## Warning:
## ! The following features were not be found:NA, ENSG00000156738, NA, NA, NA.
p2 <- SCpubr::do_DotPlot(sample = srat,
group.by = "fetal_full_predicted.annotation.l1",
features = markers,
axis.text.x.angle = 90,
flip = TRUE,
plot.title = "fetal_full_predicted.annotation.l1"
)## Warning:
## ! The following features were not be found:NA, ENSG00000156738, NA, NA, NA.
p3 <- SCpubr::do_DotPlot(sample = srat,
group.by = "seurat_clusters",
features = markers,
axis.text.x.angle = 90,
flip = TRUE,
plot.title = "seurat_clusters"
)## Warning:
## ! The following features were not be found:NA, ENSG00000156738, NA, NA, NA.
p4 <- SCpubr::do_DotPlot(sample = srat,
group.by = "cellassign_celltype_annotation",
features = markers,
axis.text.x.angle = 90,
flip = TRUE,
plot.title = "cellassign_celltype_annotation"
)## Warning:
## ! The following features were not be found:NA, ENSG00000156738, NA, NA, NA.
p1 <- do_SankeyPlot(sample = srat,
first_group = "fetal_kidney_predicted.compartment",
last_group = "seurat_clusters",
type = "sankey",text_size = 5, use_labels = TRUE
)
p2 <- do_SankeyPlot(sample = srat,
first_group = "fetal_full_predicted.annotation.l1",
last_group = "seurat_clusters",
type = "sankey",text_size = 5, use_labels = TRUE
)
p3 <- do_SankeyPlot(sample = srat,
first_group = "singler_celltype_annotation",
last_group = "seurat_clusters",
type = "sankey",text_size = 4, use_labels = TRUE
)
p4 <- do_SankeyPlot(sample = srat,
first_group = "cellassign_celltype_annotation",
last_group = "seurat_clusters",
type = "sankey",text_size = 5, use_labels = TRUE
)
(p1|p2)/(p3|p4)## Warning: Removed 1 row containing missing values or values outside the scale range (`geom_label()`).
p1 <- do_SankeyPlot(sample = srat,
first_group = "fetal_kidney_predicted.compartment",
last_group = "fetal_full_predicted.annotation.l1",
type = "sankey",text_size = 6, use_labels = TRUE
)
p2 <- do_SankeyPlot(sample = srat,
first_group = "fetal_kidney_predicted.compartment",
last_group = "fetal_kidney_predicted.cell_type",
type = "sankey",text_size = 6, use_labels = TRUE )
p1 | p2
#### Heatmaps
p1 <- do_Table_Heatmap( srat,
last_group = "fetal_kidney_predicted.compartment",
first_group = "seurat_clusters"
)
p2 <- do_Table_Heatmap( srat,
last_group = "fetal_full_predicted.annotation.l1",
first_group = "seurat_clusters"
)
p3 <- do_Table_Heatmap( srat,
last_group = "singler_celltype_annotation",
first_group = "seurat_clusters"
)
p4 <- do_Table_Heatmap( srat,
last_group = "cellassign_celltype_annotation",
first_group = "seurat_clusters"
)
(p1|p2)/(p3|p4)# record the versions of the packages used in this analysis and other environment information
sessionInfo()## R version 4.4.1 (2024-06-14)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 22.04.4 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: Europe/Vienna
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] viridis_0.6.5 viridisLite_0.4.2 ggsankey_0.0.99999 pkgload_1.4.0 ggalluvial_0.12.5 org.Hs.eg.db_3.19.1
## [7] AnnotationDbi_1.66.0 clusterProfiler_4.12.6 enrichplot_1.24.4 msigdbr_7.5.1 sctransform_0.4.1 SeuratData_0.2.2.9001
## [13] SingleCellExperiment_1.26.0 SummarizedExperiment_1.34.0 Biobase_2.64.0 GenomicRanges_1.56.1 GenomeInfoDb_1.40.1 IRanges_2.38.1
## [19] S4Vectors_0.42.1 BiocGenerics_0.50.0 MatrixGenerics_1.16.0 matrixStats_1.3.0 patchwork_1.2.0 lubridate_1.9.3
## [25] forcats_1.0.0 stringr_1.5.1 dplyr_1.1.4 purrr_1.0.2 readr_2.1.5 tidyr_1.3.1
## [31] tibble_3.2.1 ggplot2_3.5.1 tidyverse_2.0.0 SCpubr_2.0.2 Azimuth_0.5.0 shinyBS_0.61.1
## [37] Seurat_5.1.0 SeuratObject_5.0.2 sp_2.1-4 optparse_1.7.5
##
## loaded via a namespace (and not attached):
## [1] R.methodsS3_1.8.2 vroom_1.6.5 urlchecker_1.0.1 poweRlaw_0.80.0 goftest_1.2-3
## [6] DT_0.33 Biostrings_2.72.1 vctrs_0.6.5 spatstat.random_3.3-1 digest_0.6.37
## [11] png_0.1-8 ggrepel_0.9.5 deldir_2.0-4 parallelly_1.38.0 renv_1.0.7
## [16] MASS_7.3-61 Signac_1.14.0 reshape2_1.4.4 httpuv_1.6.15 qvalue_2.36.0
## [21] withr_3.0.1 xfun_0.47 ggfun_0.1.6 ellipsis_0.3.2 survival_3.7-0
## [26] EnsDb.Hsapiens.v86_2.99.0 memoise_2.0.1 gson_0.1.0 profvis_0.3.8 DElegate_1.2.1
## [31] tidytree_0.4.6 zoo_1.8-12 gtools_3.9.5 pbapply_1.7-2 R.oo_1.26.0
## [36] KEGGREST_1.44.1 promises_1.3.0 httr_1.4.7 restfulr_0.0.15 globals_0.16.3
## [41] fitdistrplus_1.2-1 rhdf5filters_1.16.0 ps_1.7.7 rhdf5_2.48.0 rstudioapi_0.16.0
## [46] UCSC.utils_1.0.0 miniUI_0.1.1.1 generics_0.1.3 DOSE_3.30.5 processx_3.8.4
## [51] babelgene_22.9 curl_5.2.2 zlibbioc_1.50.0 ggraph_2.2.1 polyclip_1.10-7
## [56] GenomeInfoDbData_1.2.12 SparseArray_1.4.8 desc_1.4.3 xtable_1.8-4 pracma_2.4.4
## [61] evaluate_0.24.0 S4Arrays_1.4.1 hms_1.1.3 irlba_2.3.5.1 colorspace_2.1-1
## [66] hdf5r_1.3.11 getopt_1.20.4 ROCR_1.0-11 reticulate_1.38.0 spatstat.data_3.1-2
## [71] magrittr_2.0.3 lmtest_0.9-40 later_1.3.2 ggtree_3.12.0 lattice_0.22-6
## [76] spatstat.geom_3.3-2 future.apply_1.11.2 shadowtext_0.1.4 scattermore_1.2 XML_3.99-0.17
## [81] cowplot_1.1.3 RcppAnnoy_0.0.22 pillar_1.9.0 nlme_3.1-166 pwalign_1.0.0
## [86] caTools_1.18.2 compiler_4.4.1 RSpectra_0.16-2 stringi_1.8.4 devtools_2.4.5
## [91] tensor_1.5 GenomicAlignments_1.40.0 plyr_1.8.9 crayon_1.5.3 abind_1.4-5
## [96] BiocIO_1.14.0 gridGraphics_0.5-1 googledrive_2.1.1 locfit_1.5-9.10 graphlayouts_1.1.1
## [101] bit_4.0.5 fastmatch_1.1-4 codetools_0.2-20 crosstalk_1.2.1 bslib_0.8.0
## [106] plotly_4.10.4 mime_0.12 splines_4.4.1 Rcpp_1.0.13 fastDummies_1.7.4
## [111] sparseMatrixStats_1.16.0 cellranger_1.1.0 knitr_1.48 blob_1.2.4 utf8_1.2.4
## [116] seqLogo_1.70.0 AnnotationFilter_1.28.0 fs_1.6.4 listenv_0.9.1 pkgbuild_1.4.4
## [121] ggplotify_0.1.2 Matrix_1.7-0 callr_3.7.6 statmod_1.5.0 tzdb_0.4.0
## [126] tweenr_2.0.3 pkgconfig_2.0.3 tools_4.4.1 cachem_1.1.0 RSQLite_2.3.7
## [131] DBI_1.2.3 fastmap_1.2.0 rmarkdown_2.28 scales_1.3.0 grid_4.4.1
## [136] usethis_2.2.3 ica_1.0-3 shinydashboard_0.7.2 Rsamtools_2.20.0 sass_0.4.9
## [141] dotCall64_1.1-1 RANN_2.6.2 farver_2.1.2 scatterpie_0.2.4 tidygraph_1.3.1
## [146] yaml_2.3.10 rtracklayer_1.64.0 cli_3.6.3 leiden_0.4.3.1 lifecycle_1.0.4
## [151] uwot_0.2.2 sessioninfo_1.2.2 presto_1.0.0 BSgenome.Hsapiens.UCSC.hg38_1.4.5 BiocParallel_1.38.0
## [156] annotate_1.82.0 timechange_0.3.0 gtable_0.3.5 rjson_0.2.22 ggridges_0.5.6
## [161] progressr_0.14.0 limma_3.60.4 parallel_4.4.1 ape_5.8 edgeR_4.2.1
## [166] jsonlite_1.8.8 RcppHNSW_0.6.0 TFBSTools_1.42.0 bitops_1.0-8 assertthat_0.2.1
## [171] bit64_4.0.5 Rtsne_0.17 yulab.utils_0.1.7 spatstat.utils_3.1-0 CNEr_1.40.0
## [176] highr_0.11 jquerylib_0.1.4 GOSemSim_2.30.2 shinyjs_2.1.0 SeuratDisk_0.0.0.9021
## [181] spatstat.univar_3.0-0 R.utils_2.12.3 lazyeval_0.2.2 shiny_1.9.1 htmltools_0.5.8.1
## [186] GO.db_3.19.1 rappdirs_0.3.3 ensembldb_2.28.1 glue_1.7.0 TFMPvalue_0.0.9
## [191] spam_2.10-0 googlesheets4_1.1.1 httr2_1.0.3 XVector_0.44.0 RCurl_1.98-1.16
## [196] rprojroot_2.0.4 treeio_1.28.0 BSgenome_1.72.0 gridExtra_2.3 JASPAR2020_0.99.10
## [201] igraph_2.0.3 R6_2.5.1 labeling_0.4.3 RcppRoll_0.3.1 GenomicFeatures_1.56.0
## [206] cluster_2.1.6 Rhdf5lib_1.26.0 gargle_1.5.2 aplot_0.2.3 DirichletMultinomial_1.46.0
## [211] DelayedArray_0.30.1 tidyselect_1.2.1 ProtGenerics_1.36.0 ggforce_0.4.2 future_1.34.0
## [216] munsell_0.5.1 KernSmooth_2.23-24 data.table_1.16.0 htmlwidgets_1.6.4 fgsea_1.30.0
## [221] RColorBrewer_1.1-3 rlang_1.1.4 spatstat.sparse_3.1-0 spatstat.explore_3.3-2 remotes_2.5.0
## [226] fansi_1.0.6